/**
 *******************************************************************************
 * @file uart_driver.c
 * @author .ISS_AUTO (hudandan@issauto.com)
 * @brief This file is interfaces for uart driver.
 *        MCU: N32G45x
 *
 * @date 2023-06-08    1. Uart初始配置函数接口：
 *                        void UartX_Configuration(uint8_t chan, uint32_t baud_rate);
 *                     2. Uart中断状态标志位检查接口：
 *                        int Check_USARTx_TXC_IntStatus(uint8_t chan);
 *                     3. Uart发送接口：
 *                        void USARTx_SendData(uint8_t chan, uint16_t dat);
 *                     4. Uart接收接口：
 *                        uint16_t USARTx_ReceiveData(uint8_t chan);
 *       2024-02-26    1. Uart阻塞发送接口：
 *                        void USARTx_SendData_Blocking(uint8_t chan, uint16_t dat);
 *       2024-06-20    1. 优化Uart初始配置函数接口，
 *                        分为 “Uart(外设IO)初始配置接口” 与 “Uart中断配置接口”；
 *       2024-07-15    1. 新增当前通道的波特率读取，
 *                        USARTx_GetBaudRate()；
 *       2025-01-03    1. 优化UART发送管理，并更新阻塞式和非阻塞式发送接口；
 *       2025-03-25    1. UART1 配置添加偶检验示例；
 *
 * @copyright Copyright (c) 2022 ISSAUTO TECH Co., Ltd. All rights reserved.
 *
 *******************************************************************************
 */
/* Private Includes ----------------------------------------------------------*/
#include "uart_driver.h"
#include <stdio.h>

/* Private macro -------------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
/** @brief USARTx Map */
const USART_Module *pUSARTx_MAP[] = {
    USART1, USART2, USART3, UART4, UART5, UART6, UART7
};

const IRQn_Type USARTx_IRQn[] = {
    USART1_IRQn, USART2_IRQn, USART3_IRQn, UART4_IRQn, UART5_IRQn, UART6_IRQn, UART7_IRQn
};

volatile UART_DriverTypeDef uartx_driver[UART_CHANNEL_MAX] = {0};

/* Private variables ---------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/******************************************************
*              USARTx peripheral 接口定义
******************************************************/

#if (UART_1_CFG_ENABLE)
/**
 * @brief Uart1 channel Configuration
 *
 * @param[in] baud_rate
 * @return
 */
void Uart1_Configuration(uint32_t baud_rate)
{
    USART_InitType USART_InitStructure;
    GPIO_InitType GPIO_InitStructure;

    /* Initialization of uart1 .... */
    // USART_DeInit(USART1);

    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_AFIO, ENABLE);

    /** USART1 GPIO Configures
    PA9  ------> USART1_TX
    PA10 ------> USART1_RX
    */

    /* Configure USART1 Tx as alternate function push-pull */
    GPIO_InitStructure.Pin        = GPIO_PIN_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Configure USART1 Rx as input floating */
    GPIO_InitStructure.Pin       = GPIO_PIN_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Enable USART1 Clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1, ENABLE);

    /* USART1 configuration */
    USART_InitStructure.BaudRate            = baud_rate;
    /* [偶校验]
     * USART_InitStructure.WordLength          = USART_WL_9B;
     * USART_InitStructure.StopBits            = USART_STPB_1;
     * USART_InitStructure.Parity              = USART_PE_EVEN;
     */
    /* [无校验] */
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Configure USART1 */
    USART_Init(USART1, &USART_InitStructure);

    /* Enable the USART1 */
    USART_Enable(USART1, ENABLE);

    // uartx_driver[UART_CHANNEL_1].USARTx = USART1;

}
#endif

#if (UART_2_CFG_ENABLE)
/**
 * @brief Uart2 channel Configuration
 *
 * @param[in] baud_rate
 * @return
 */
void Uart2_Configuration(uint32_t baud_rate)
{
    USART_InitType USART_InitStructure;
    GPIO_InitType GPIO_InitStructure;

    /* Initialization of uart2 .... */
    // USART_DeInit(USART2);

    #if (1)
    /** USART2 GPIO Configures
    PA2 ------> USART2_TX
    PA3 ------> USART2_RX
    */

    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_AFIO, ENABLE);

    /* Configure USART2 Tx as alternate function push-pull */
    GPIO_InitStructure.Pin        = GPIO_PIN_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Configure USART2 Rx as input floating */
    GPIO_InitStructure.Pin       = GPIO_PIN_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    #else
    /** USART2 GPIO Configures
    PB4 ------> USART2_TX
    PB5 ------> USART2_RX
    */

    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_AFIO, ENABLE);

    /* Configure USART2 Tx as alternate function push-pull */
    GPIO_InitStructure.Pin        = GPIO_PIN_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Configure USART2 Rx as input floating */
    GPIO_InitStructure.Pin       = GPIO_PIN_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Remap USART2 GPIOs */
    // GPIO_ConfigPinRemap(GPIO_RMP_SW_JTAG_NO_NJTRST, ENABLE); //!< Release PB4
    GPIO_ConfigPinRemap(GPIO_RMP3_USART2, ENABLE);

    #endif

    /* Enable USART2 Clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART2, ENABLE);

    /* USART2 configuration */
    USART_InitStructure.BaudRate            = baud_rate;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Configure USART2 */
    USART_Init(USART2, &USART_InitStructure);

    /* Enable the USART2 */
    USART_Enable(USART2, ENABLE);

    // uartx_driver[UART_CHANNEL_2].USARTx = USART2;

}
#endif

#if (UART_3_CFG_ENABLE)
/**
 * @brief Uart3 channel Configuration
 *
 * @param[in] baud_rate
 * @return
 */
void Uart3_Configuration(uint32_t baud_rate)
{
    USART_InitType USART_InitStructure;
    GPIO_InitType GPIO_InitStructure;

    /* Initialization of uart3 .... */
    // USART_DeInit(USART3);

    /** USART3 GPIO Configures
    PB10 ------> USART3_TX
    PB11 ------> USART3_RX
    */

    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_AFIO, ENABLE);

    /* Configure USART3 Tx as alternate function push-pull */
    GPIO_InitStructure.Pin        = GPIO_PIN_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Configure USART3 Rx as input floating */
    GPIO_InitStructure.Pin       = GPIO_PIN_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Enable USART3 Clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART3, ENABLE);

    /* USART3 configuration */
    USART_InitStructure.BaudRate            = baud_rate;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Configure USART3 */
    USART_Init(USART3, &USART_InitStructure);

    /* Enable the USART3 */
    USART_Enable(USART3, ENABLE);

    // uartx_driver[UART_CHANNEL_3].USARTx = USART3;

}
#endif

#if (UART_5_CFG_ENABLE)
/**
 * @brief Uart5 channel Configuration
 *
 * @param[in] baud_rate
 * @return
 */
void Uart5_Configuration(uint32_t baud_rate)
{
    USART_InitType USART_InitStructure;
    GPIO_InitType GPIO_InitStructure;

    /* Initialization of uart5 .... */
    // USART_DeInit(UART5);

    /** UART5 GPIO Configures
    PB13 ------> UART5_TX
    PB14 ------> UART5_RX
    */

    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_AFIO, ENABLE);

    /* Configure UART5 Tx as alternate function push-pull */
    GPIO_InitStructure.Pin        = GPIO_PIN_13;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Configure UART5 Rx as input floating */
    GPIO_InitStructure.Pin       = GPIO_PIN_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Remap UART5 GPIOs */
    GPIO_ConfigPinRemap(GPIO_RMP1_UART5, ENABLE);

    /* Enable UART5 Clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_UART5, ENABLE);

    /* UART5 configuration */
    USART_InitStructure.BaudRate            = baud_rate;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Configure UART5 */
    USART_Init(UART5, &USART_InitStructure);

    /* Enable the UART5 */
    USART_Enable(UART5, ENABLE);

    // uartx_driver[UART_CHANNEL_5].USARTx = UART5;

}
#endif

#if (UART_6_CFG_ENABLE)
/**
 * @brief Uart6 channel Configuration
 *
 * @param[in] baud_rate
 * @return
 */
void Uart6_Configuration(uint32_t baud_rate)
{
    USART_InitType USART_InitStructure;
    GPIO_InitType GPIO_InitStructure;

    /* Initialization of uart6 .... */
    USART_DeInit(UART6);

    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_AFIO, ENABLE);

    /** UART6 GPIO Configures
    PB0 ------> UART6_TX
    PB1 ------> UART6_RX
    */

    /* Configure UART6 Tx as alternate function push-pull */
    GPIO_InitStructure.Pin        = GPIO_PIN_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Configure UART6 Rx as input floating */
    GPIO_InitStructure.Pin       = GPIO_PIN_1;
    // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    /* Configure UART6 Rx as input pull-up */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //!< 使能上拉，保持高电平
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Remap UART6 GPIOs */
    GPIO_ConfigPinRemap(GPIO_RMP3_UART6, ENABLE);

    /* Enable UART6 Clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_UART6, ENABLE);

    /* UART6 configuration */
    USART_InitStructure.BaudRate            = baud_rate;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;

    /* Configure UART6 */
    USART_Init(UART6, &USART_InitStructure);

    /* Enable the UART6 */
    USART_Enable(UART6, ENABLE);

    // uartx_driver[UART_CHANNEL_6].USARTx = UART6;

}
#endif

/**
 * @brief Specified uart channel Configuration
 *
 * @param[in] chan specified uart channel
 * @param[in] baud_rate
 * @return
 */
void UartX_Configuration(uint8_t chan, uint32_t baud_rate)
{
    ISS_ASSERT(chan < UART_CHANNEL_MAX);

    switch (chan) {
            #if (UART_1_CFG_ENABLE)

        case UART_CHANNEL_1:
            Uart1_Configuration(baud_rate);
            break;
            #endif

            #if (UART_2_CFG_ENABLE)

        case UART_CHANNEL_2:
            Uart2_Configuration(baud_rate);
            break;
            #endif

            #if (UART_3_CFG_ENABLE)

        case UART_CHANNEL_3:
            Uart3_Configuration(baud_rate);
            break;
            #endif

            #if (UART_5_CFG_ENABLE)

        case UART_CHANNEL_5:
            Uart5_Configuration(baud_rate);
            break;
            #endif

            #if (UART_6_CFG_ENABLE)

        case UART_CHANNEL_6:
            Uart6_Configuration(baud_rate);
            break;

            #endif

        default:
            break;
    }

    uartx_driver[chan].BaudRate = baud_rate;
    uartx_driver[chan].gState   = UART_STATE_READY;

}

/**
 * @brief Specified uart channel Interrupt Configuration
 *
 * @param[in] chan specified uart channel
 * @param[in] r_cmd - configure "Receive Data register not empty interrupt"
 * @param[in] priority
 * @return
 */
void UartX_ConfigurationInt(uint8_t chan, FunctionalState r_cmd, uint8_t priority)
{
    NVIC_InitType NVIC_InitStructure;

    ISS_ASSERT(chan < UART_CHANNEL_MAX);

    USART_ConfigInt((USART_Module *)pUSARTx_MAP[chan], USART_INT_RXDNE, r_cmd); //!< 接收数据寄存器非空中断 (Receive Data register not empty interrupt)

    /* Configure the NVIC Preemption Priority Bits */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

    /* Enable the USART1 Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel            = USARTx_IRQn[chan];
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = priority;
    NVIC_InitStructure.NVIC_IRQChannelCmd         = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

}

/**
 * @brief return the USART Baud Rate
 *
 * @param chan specified uart channel
 * @return baudRate
 */
uint32_t USARTx_GetBaudRate(uint8_t chan)
{
    ISS_ASSERT(chan < UART_CHANNEL_MAX);

    return uartx_driver[chan].BaudRate;
}

/**
 * @brief Configure the USART Baud Rate
 *
 * @param chan specified uart channel
 * @param baudRate
 */
void USARTx_SetBaudRate(uint8_t chan, uint32_t baudRate)
{
    uint32_t tmpregister = 0x00, apbclock = 0x00;
    uint32_t integerdivider    = 0x00;
    uint32_t fractionaldivider = 0x00;
    uint32_t usartxbase        = 0;
    RCC_ClocksType RCC_ClocksStatus;

    ISS_ASSERT(chan < UART_CHANNEL_MAX);

    USART_Module *USARTx = (USART_Module *)pUSARTx_MAP[chan];

    usartxbase = (uint32_t)USARTx;

    /*---------------------------- USART PBC Configuration -----------------------*/
    /* Configure the USART Baud Rate -------------------------------------------*/
    RCC_GetClocksFreqValue(&RCC_ClocksStatus);

    if ((usartxbase == USART1_BASE) || (usartxbase == UART6_BASE) || (usartxbase == UART7_BASE)) {
        apbclock = RCC_ClocksStatus.Pclk2Freq;
    } else {
        apbclock = RCC_ClocksStatus.Pclk1Freq;
    }

    /* Determine the integer part */
    integerdivider = ((25 * apbclock) / (4 * (baudRate)));
    tmpregister = (integerdivider / 100) << 4;

    /* Determine the fractional part */
    fractionaldivider = (((((integerdivider - (100 * (tmpregister >> 4))) * 16) + 50) / 100));

    /*Determine whether the fractional part needs to carried*/
    if ((fractionaldivider >> 4) == 1) {
        tmpregister = ((integerdivider / 100) + 1) << 4;
    }

    /* Implement the fractional part in the register */
    tmpregister |= fractionaldivider & ((uint8_t)0x0F);

    /* Write to USART PBC */
    USARTx->BRCF = (uint16_t)tmpregister;


    /* Update the baud rate */
    uartx_driver[chan].BaudRate = baudRate;

}

/******************************************************
*                   USARTx API
******************************************************/

#define UART_TIMEOUT_VALUE     10U

/**
 * @brief  Sends an amount of data in blocking mode.
 *
 * @param[in] chan specified uart channel
 * @param[in] pDat the data to transmit
 * @param[in] pDatLen Amount of data elements (u8) to be sent
 * @return uint16_t
 */
uint16_t USARTx_SendData_Blocking(uint8_t chan, const uint8_t *pDat, uint16_t pDatLen)
{
    ISS_ASSERT(chan < UART_CHANNEL_MAX);

    if ((pDat == NULL) || (pDatLen == 0U)) {
        return 0;
    }

    uint8_t *pTxDat = (uint8_t *)pDat;
    uint16_t txCount = 0U;

    /* Check that a Tx process is not already ongoing */
    if (uartx_driver[chan].gState == UART_STATE_READY) {
        uint32_t tickstart = 0U;
        uint32_t timeout = 0U;

        uartx_driver[chan].gState = UART_STATE_BUSY_TX;

        /* Init tickstart for timeout management */
        tickstart = STIMER_GET();
        timeout = UART_TIMEOUT_VALUE * pDatLen;

        while (txCount < pDatLen) {
            /* Waiting to send data register is empty */
            while (USART_GetFlagStatus((USART_Module *)pUSARTx_MAP[chan], USART_FLAG_TXDE) == RESET) {
                if (STIMER_IS_TIMEOUT(tickstart, timeout)) {
                    return 0;
                }
            }

            /* Send one byte data to usart */
            USART_SendData((USART_Module *)pUSARTx_MAP[chan], (uint16_t)(*pTxDat++));
            txCount ++;
        }

        /* At end of Tx process, restore gState to Ready */
        uartx_driver[chan].gState = UART_STATE_READY;
    }

    return txCount;
}

/**
 * @brief  Transmits single data through the USARTx peripheral.
 *
 * @param[in] chan specified uart channel
 * @param[in] dat the data to transmit.
 */
void USARTx_SendData(uint8_t chan, uint16_t dat)
{
    ISS_ASSERT(chan < UART_CHANNEL_MAX);

    USART_SendData((USART_Module *)pUSARTx_MAP[chan], dat);
}

/**
 * @brief Returns the most recent received data by the USARTx peripheral.
 *
 * @param[in] chan specified uart channel
 * @return none
 */
uint16_t USARTx_ReceiveData(uint8_t chan)
{
    ISS_ASSERT(chan < UART_CHANNEL_MAX);

    /* Read one byte from the receive data register */
    return (uint16_t)USART_ReceiveData((USART_Module *)pUSARTx_MAP[chan]);
}

/**
 * @brief Retargets the C library printf function to the USART
 *
 * @param ch
 * @param f
 * @return int
 */
#if defined (__USE_NO_MICROLIB)
#pragma import(__use_no_semihosting)

struct __FILE {
    int handle;
};

FILE __stdout;

void _sys_exit(int x)
{
    x = x;
}
#endif

int fputc(int ch, FILE *f)
{
    #ifdef UARTx_PRINTF
    uint8_t temp = (uint8_t)ch;
    (void)USARTx_SendData_Blocking(UARTx_PRINTF, (const uint8_t *)&temp, 1);
    #endif

    return (ch);
}

/* END OF FILE ---------------------------------------------------------------*/
